#include "CSimpleTerrain.h"


CSimpleTerrain::~CSimpleTerrain()
{
	Engine->SafeRelease(TerrainVb);
	Engine->SafeRelease(TerrainIb);
	Engine->SafeRelease(TerrainTexture);
	Engine->SafeRelease(TerrainDetailTexture);
}
bool CSimpleTerrain::SetupTerrain(int MapSize,int HeightScale,int MapScale,
		int TextureIncrement,int DetailIncrement,std::string HeightMapFile,
		std::string Texture,std::string DetailTexture)

{
	m_MapSize  = MapSize;
	m_MapScale = MapScale;
	m_HeightScale=HeightScale;

	m_TextureIncrement = TextureIncrement;
	m_DetailIncrement  = DetailIncrement;

	NumVertices =  m_MapSize*m_MapSize;
	NumTriangles= ((m_MapSize-1)*(m_MapSize-1))*6;
	VertexData  = new TerrainVertex[NumVertices];	
	IndexData   = new DWORD[NumTriangles];
	HeightMap   = new unsigned char[m_MapSize*m_MapSize];

	LoadHeightMap(HeightMapFile);	
	SetupVertexBuffer();
	SetupIndexBuffer();
	ComputeNormals();
	ComputeTangentBinormal();	
	SetSamplerState();	


	D3DXCreateEffectFromFile(Engine->GetDevice(),"Shader/ParallaxMapDirectionalLight.fx",0,0,0,0,&TerrainEffect,0);	
	D3DXCreateTextureFromFile(Engine->GetDevice(),Texture.c_str(),&TerrainTexture);
	D3DXCreateTextureFromFile(Engine->GetDevice(),DetailTexture.c_str(),&TerrainDetailTexture);
	D3DXCreateTextureFromFile(Engine->GetDevice(),"Terrain/PHeightMap.jpg",&blendmapTexture);
	
	
	vertexDecleration = 0;
	
	D3DVERTEXELEMENT9 VertexDecl[] = {
	 { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
	 { 0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
	 { 0, 20, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
	 { 0, 32, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0 },
	 { 0, 44, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0 },
	 D3DDECL_END()};

	Engine->GetDevice()->CreateVertexDeclaration(VertexDecl,&vertexDecleration);
	
	
	return true;
}
void CSimpleTerrain::LoadHeightMap(std::string HeightMapFile)
{	
	/*
	FILE    *MapFile;
	MapFile = fopen(HeightMapFile.c_str(),"rb");
	fread(HeightMap,1,m_MapSize*m_MapSize,MapFile);	
	fclose(MapFile);
	*/

	LPDIRECT3DTEXTURE9 hm;	
	D3DXCreateTextureFromFile(Engine->GetDevice(),HeightMapFile.c_str(),&hm);		

	LPDIRECT3DSURFACE9 surface;
	hm->GetSurfaceLevel(0, &surface);	

	HDC hdc;
	surface->GetDC(&hdc);	
	for (unsigned int z=0; z < m_MapSize; z++)
	{
		for (unsigned int x=0; x < m_MapSize; x++)
		{
			unsigned int Index= x+(m_MapSize*z);
			DWORD C = GetPixel(hdc,x,z);	C = C>>16; 			
			HeightMap[Index] = C;	
		}
	}
	surface->ReleaseDC(hdc);
	surface->Release();
	hm->Release();
	
}
void CSimpleTerrain::SetupVertexBuffer()
{
	Engine->GetDevice()->CreateVertexBuffer(sizeof(TerrainVertex)*NumVertices,
		D3DUSAGE_WRITEONLY,TerrainVertex::FVF,D3DPOOL_MANAGED,&TerrainVb,0);

	for (unsigned int z=0; z < m_MapSize; z++)
	{
		for (unsigned int x=0; x < m_MapSize; x++)
		{
			unsigned int Index= x+(m_MapSize*z);

			VertexData[Index].Position= D3DXVECTOR3(x*m_MapScale,HeightMap[Index]*m_HeightScale,z*m_MapScale);
			VertexData[Index].Normal  = D3DXVECTOR3(0.0f,1.0f,0.0f);
			VertexData[Index].Tangent = D3DXVECTOR3(1.0f,0.0f,0.0f);
			VertexData[Index].Binormal= D3DXVECTOR3(0.0f,0.0f,1.0f);
			VertexData[Index].color= 0xFFFFFFFF;	
			VertexData[Index].tu =(float)(x/((float)m_MapSize-1)) * m_TextureIncrement;
			VertexData[Index].tv =(float)(z/((float)m_MapSize-1)) * m_TextureIncrement;			
			VertexData[Index].tu2 =(float)(x/((float)m_MapSize-1))* m_DetailIncrement;
			VertexData[Index].tv2 =(float)(z/((float)m_MapSize-1))* m_DetailIncrement;
		
		}
	}
	VOID* VertexPtr;
	TerrainVb->Lock(0,sizeof(TerrainVertex)*NumVertices,(void**)&VertexPtr,0);
	memcpy(VertexPtr,VertexData,sizeof(TerrainVertex)*NumVertices);
	TerrainVb->Unlock();

}
void CSimpleTerrain::SetupIndexBuffer()
{
	Engine->GetDevice()->CreateIndexBuffer(sizeof(DWORD)*NumTriangles,
		0,D3DFMT_INDEX32,D3DPOOL_MANAGED,&TerrainIb,0);

	unsigned int loop =0;
	for(unsigned int z=0; z < m_MapSize-1; z++)
	{
		for(unsigned int x=0; x < m_MapSize-1; x++)
		{
			IndexData[loop] =(m_MapSize*z)+x+m_MapSize;
			loop++;
			IndexData[loop] =(m_MapSize*z)+x+1;
			loop++;
			IndexData[loop] =(m_MapSize*z)+x;
			loop++;
	
			IndexData[loop] =(m_MapSize*z)+x+m_MapSize;
			loop++;
			IndexData[loop] =(m_MapSize*z)+x+m_MapSize+1;
			loop++;
			IndexData[loop] =(m_MapSize*z)+x+1;
			loop++;		
		}
	}
	VOID* IndexDataPtr;
	TerrainIb->Lock(0,sizeof(DWORD)*NumTriangles,(void**)&IndexDataPtr,0);
	memcpy(IndexDataPtr,IndexData,sizeof(DWORD)*NumTriangles);
	TerrainIb->Unlock();

}
void CSimpleTerrain::ComputeNormals()
{
	VOID* IndexDataPtr;
	TerrainIb->Lock(0,sizeof(DWORD)*NumTriangles,(void**)&IndexDataPtr,0);

	VOID* VertexPtr;
	TerrainVb->Lock(0,sizeof(TerrainVertex)*NumVertices,(void**)&VertexPtr,0);

	D3DXVECTOR3 Side1;
	D3DXVECTOR3 Side2;
	D3DXVECTOR3 Cross;

	
	for(unsigned int i=0; i < NumTriangles/3; i++)
	{
		int Index1 = IndexData[i * 3]; 
		int Index2 = IndexData[i * 3 + 1];
		int Index3 = IndexData[i * 3 + 2];

		Side1 = VertexData[Index1].Position - VertexData[Index2].Position;
		Side2 = VertexData[Index1].Position - VertexData[Index3].Position;

		D3DXVec3Cross(&Cross,&Side1,&Side2);
		D3DXVec3Normalize(&Cross,&Cross); 		
		
		VertexData[Index1].Normal=Cross;  
		VertexData[Index2].Normal=Cross;
		VertexData[Index3].Normal=Cross;
	}
	
	memcpy(IndexDataPtr,IndexData,sizeof(DWORD)*NumTriangles);
	TerrainIb->Unlock();

	memcpy(VertexPtr,VertexData,sizeof(TerrainVertex)*NumVertices);
	TerrainVb->Unlock();
	
	m_LineVertex = new LineVertex[NumVertices*2];
	for (int k=0; k < NumVertices; k++)
	{
		m_LineVertex[k*2+0].VPos=VertexData[k].Position;
		m_LineVertex[k*2+0].color=D3DCOLOR_XRGB(255,0,0);
		m_LineVertex[k*2+1].VPos=(VertexData[k].Position+=(VertexData[k].Normal*30));
		m_LineVertex[k*2+1].color=D3DCOLOR_XRGB(255,0,0);
	}
}

void CSimpleTerrain::ComputeTangentBinormal()
{
	VOID* VertexPtr;
	TerrainVb->Lock(0,sizeof(TerrainVertex)*NumVertices,(void**)&VertexPtr,0);

	for (unsigned int z=0; z < m_MapSize; z++)
	{
		for (unsigned int x=0; x < m_MapSize; x++)
		{
			unsigned int Index= x+(m_MapSize*z);

			D3DXVECTOR3 V1=VertexData[Index].Position;

			
			if( x < m_MapSize-1 )
			{
				D3DXVECTOR3 V2=VertexData[Index+1].Position;
				VertexData[Index].Tangent=(V2-V1);
			}
			else
			{
				D3DXVECTOR3 V2=VertexData[Index-1].Position;
				VertexData[Index].Tangent=(V1-V2);				
			}
			
			D3DXVec3Normalize(&VertexData[Index].Tangent,&VertexData[Index].Tangent);

			D3DXVECTOR3 Out;
			D3DXVec3Cross(&Out,&VertexData[Index].Tangent,&VertexData[Index].Normal);
			VertexData[Index].Binormal+=Out;
			D3DXVec3Normalize(&VertexData[Index].Binormal,&VertexData[Index].Binormal);
		}
	}
	memcpy(VertexPtr,VertexData,sizeof(TerrainVertex)*NumVertices);
	TerrainVb->Unlock();
}
void CSimpleTerrain::SetSamplerState()
{
	Engine->GetDevice()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	Engine->GetDevice()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	Engine->GetDevice()->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
	Engine->GetDevice()->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, D3DTEXF_ANISOTROPIC);

	Engine->GetDevice()->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	Engine->GetDevice()->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	Engine->GetDevice()->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
	Engine->GetDevice()->SetSamplerState(1, D3DSAMP_MAXANISOTROPY, D3DTEXF_ANISOTROPIC);
	
	Engine->GetDevice()->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); 
	Engine->GetDevice()->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); 
	Engine->GetDevice()->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_CURRENT ); 
	Engine->GetDevice()->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_TEXTURE );
	Engine->GetDevice()->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_MODULATE );	
	Engine->GetDevice()->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_ADDSIGNED);
		
}
void CSimpleTerrain::UpdateTerrain()
{
	
	if(GameInput->KeyDown(DIK_ADD))     {m_HeightScale++;};
	if(GameInput->KeyDown(DIK_SUBTRACT)){m_HeightScale--;};

	if(GameInput->KeyDown(DIK_O)){m_MapScale++;};
	if(GameInput->KeyDown(DIK_P)){m_MapScale--;};

	if(GameInput->KeyDown(DIK_T)) {m_TextureIncrement++;}
	if(GameInput->KeyDown(DIK_U)) {m_DetailIncrement++;}

	for (unsigned int z=0; z < m_MapSize; z++){
		for (unsigned int x=0; x < m_MapSize; x++){

			unsigned int Index= x+(m_MapSize*z);
			
			VertexData[Index].Position=D3DXVECTOR3(x*m_MapScale,HeightMap[Index]*m_HeightScale,z*m_MapScale);
			VertexData[Index].Normal  = D3DXVECTOR3(0.0f,1.0f,0.0f);
			VertexData[Index].color= 0xFFFFFFFF;	
			VertexData[Index].tu =(float)(x/((float)m_MapSize-1)) *m_TextureIncrement;
			VertexData[Index].tv =(float)(z/((float)m_MapSize-1)) *m_TextureIncrement;			
			VertexData[Index].tu2 =(float)(x/((float)m_MapSize-1))*m_DetailIncrement;
			VertexData[Index].tv2 =(float)(z/((float)m_MapSize-1))*m_DetailIncrement;		
		}
	}
	VOID* VertexPtr;
	TerrainVb->Lock(0,sizeof(TerrainVertex)*NumVertices,(void**)&VertexPtr,0);
	memcpy(VertexPtr,VertexData,sizeof(TerrainVertex)*NumVertices);
	TerrainVb->Unlock();

}
inline void SetSunLight()
{
    D3DMATERIAL9 mtrl;
    ZeroMemory( &mtrl, sizeof(D3DMATERIAL9) );
    mtrl.Diffuse.r = mtrl.Ambient.r = 255.0f;
    mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f;
    mtrl.Diffuse.b = mtrl.Ambient.b = 1.0f;
    mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;
    Engine->GetDevice()->SetMaterial( &mtrl );

   
    D3DXVECTOR3 vecDir;
    D3DLIGHT9 light;
    ZeroMemory( &light, sizeof(D3DLIGHT9) );
    light.Type       = D3DLIGHT_DIRECTIONAL;
    light.Diffuse.r  = 1.0f;
    light.Diffuse.g  = 1.0f;
    light.Diffuse.b  = 1.0f;
    vecDir = D3DXVECTOR3( 1.0f, -1.0f, 1000.0f );
    D3DXVec3Normalize( (D3DXVECTOR3*)&light.Direction, &vecDir );
    light.Range       = 100000.0f;
    Engine->GetDevice()->SetLight( 0, &light );
    Engine->GetDevice()->LightEnable( 0, TRUE );
    Engine->GetDevice()->SetRenderState( D3DRS_LIGHTING, TRUE );

    Engine->GetDevice()->SetRenderState( D3DRS_AMBIENT, 0x00404040 );	
}
void CSimpleTerrain::Render()
{	
	D3DXMATRIX Translate;
	D3DXMatrixIdentity(&Translate);
	D3DXMatrixTranslation(&Translate,0,0,0);
	Engine->GetDevice()->SetTransform(D3DTS_WORLD,&Translate);	
	

	Engine->GetDevice()->SetStreamSource(0,TerrainVb,0,sizeof(TerrainVertex));
	Engine->GetDevice()->SetIndices(TerrainIb);
	Engine->GetDevice()->SetFVF(TerrainVertex::FVF);
	Engine->GetDevice()->SetTexture(0,TerrainTexture);
	Engine->GetDevice()->SetTexture(1,TerrainDetailTexture); 
	Engine->GetDevice()->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW);	

	Engine->GetDevice()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,NumVertices,0,NumTriangles/3);
	
	
	Engine->GetDevice()->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);	
	
	if(KEYDOWN(VK_F5))
	{
		Engine->GetDevice()->LightEnable(0, FALSE);
		Engine->GetDevice()->SetRenderState(D3DRS_LIGHTING, FALSE);
		D3DXMATRIX NorMat;
		D3DXMatrixTranslation(&NorMat,0,0,0);
		Engine->GetDevice()->SetTransform(D3DTS_WORLD,&NorMat);
		Engine->GetDevice()->SetFVF(LineVertex::FVF);
		Engine->GetDevice()->DrawPrimitiveUP(D3DPT_LINELIST,NumVertices,m_LineVertex,sizeof(LineVertex));
		Engine->GetDevice()->LightEnable(0, TRUE);
		Engine->GetDevice()->SetRenderState(D3DRS_LIGHTING, TRUE);
	}
	else
	{
	}
	Engine->GetDevice()->SetTexture(0,NULL);
	Engine->GetDevice()->SetTexture(1,NULL); 	
}
float CSimpleTerrain::GetTerrainHeight( float x, float z )
{
	x = x / m_MapScale;
	z = z / m_MapScale;
	if( x < 0 || x >= m_MapSize || z < 0 || z >= m_MapSize )
	{	
	   return false;
	}
	float m_MapSizeValue = BilinearInterpolation( x, z );
	return m_MapSizeValue;
}
float CSimpleTerrain::BilinearInterpolation(float iX, float iZ)
{
	if( (int)iX+1 >= m_MapSize || (int)iZ+1 >= m_MapSize )
		return HeightMap[(int)iX+(m_MapSize*(int)iZ)];

	float temp[4];
	temp[0] =CSimpleTerrain::GetScaledHeight(iX,iZ);
	temp[1] =CSimpleTerrain::GetScaledHeight(iX+1,iZ);
	temp[2] =CSimpleTerrain::GetScaledHeight(iX+1,iZ+1);
	temp[3] =CSimpleTerrain::GetScaledHeight(iX,iZ+1);
	
	float x, z;
	x = floor(iX); 
	z = floor(iZ); 
	
	float tx = iX - x;
	float tz = iZ - z;
	float txtz = tx * tz;

	return ((temp[0] * (1 - tz - tx + txtz)) + (temp[1] * (tx - txtz)) + (temp[2] * txtz) + (temp[3] * (tz - txtz)));
}
void CSimpleTerrain::SmoothHeightMap(int Smooth)
{ 
   float value = 0.0f;
   float cellAverage = 0.0f;
   int i = 0;
   int bounds = m_MapSize * m_MapSize;

            for (int y = 0; y < m_MapSize; ++y)
            {
                for (int x = 0; x < m_MapSize; ++x)
                {
                    value = 0.0f;
                    cellAverage = 0.0f;

                    i = (y - 1) * m_MapSize + (x - 1);
                    if (i >= 0 && i < bounds)
                    {
                        value +=HeightMap[i];
                        cellAverage += 12;
                    }

                    i = (y - 1) * m_MapSize + x;
                    if (i >= 0 && i < bounds)
                    {
                        value +=HeightMap[i];
                        cellAverage += 12;
                    }

                    i = (y - 1) * m_MapSize + (x + 1);
                    if (i >= 0 && i < bounds)
                    {
                        value +=HeightMap[i];
                        cellAverage += 12;
                    }

                    i = y * m_MapSize + (x - 1);
                    if (i >= 0 && i < bounds)
                    {
                        value +=HeightMap[i];
                        cellAverage += 12;
                    }

                    i = y * m_MapSize + x;
                    if (i >= 0 && i < bounds)
                    {
                        value +=HeightMap[i];
                        cellAverage += 12;
                    }

                    i = y * m_MapSize + (x + 1);
                    if (i >= 0 && i < bounds)
                    {
                        value +=HeightMap[i];
                        cellAverage += 12;
                    }

                    i = (y + 1) * m_MapSize + (x - 1);
                    if (i >= 0 && i < bounds)
                    {
                        value +=HeightMap[i];
                        cellAverage += 12;
                    }

                    i = (y + 1) * m_MapSize + x;
                    if (i >= 0 && i < bounds)
                    {
                        value +=HeightMap[i];
                        cellAverage += 12;
                    }

                    i = (y + 1) * m_MapSize + (x + 1);
                    if (i >= 0 && i < bounds)
                    {
                        value +=HeightMap[i];
                        cellAverage += 12;
                    }

                    HeightMap[y * m_MapSize + x] = value / cellAverage;
                }
       }
}